#include "field.hpp"

namespace hms
{

template<typename MeshType>
Field<MeshType>::Field(
	const MeshBase<MeshType>& mesh,
	Index nComponents
) :
	FieldBase<Field<MeshType>>( mesh )
{
	resizeFieldValues<NonUniform>(m_values, nComponents, mesh);
}

template<typename MeshType>
Field<MeshType>::Field(
	const MeshBase<MeshType>& mesh,
	ArrayXXs&& values
) :
	FieldBase<Field<MeshType>>( mesh )
{
	assertFieldValuesSize<NonUniform>(values, mesh);
	m_values = std::move(values);
	setCorners(m_values, this->mesh(), FieldBase<Type>::cornerValue);
}

template<typename MeshType>
typename Field<MeshType>::ValueType Field<MeshType>::valuesImp() {
	return m_values;
}

template<typename MeshType>
typename Field<MeshType>::ConstValueType Field<MeshType>::valuesImp() const {
	return m_values;
}

template<typename MeshType>
auto Field<MeshType>::boundaryConditions() const ->
	const std::vector<std::unique_ptr<BoundaryCondition<MeshType>>>&
{ return m_boundaryConditions; }


template<typename MeshType>
auto Field<MeshType>::boundaryCondition( const BoundaryPatch& patch ) const ->
	const BoundaryCondition<MeshType>&
{
	assert( this->mesh().boundaryPatches().size() == m_boundaryConditions.size()
		&& "Requesting boundary condition before setting them!" );
	
	auto ptr = m_boundaryConditions[ getPatchIndex( this->mesh(), patch) ].get();
	assert( ptr && "Requested boundary condition not set!");
	if (ptr)
		return *ptr;
	else
		throw std::runtime_error( "Requested boundary condition not set!" );
}

template<typename MeshType>
auto Field<MeshType>::boundaryCondition( const std::string& patchName ) const ->
	const BoundaryCondition<MeshType>&
{
	return boundaryCondition( this->mesh().boundaryPatch(patchName) );
}

/**
 * @brief Returns true if all non-empty boundary patches have boundary
 * condition associated with them and false otherwise.
 * 
 * @tparam MeshType 
 * @return true 
 * @return false 
 */
template<typename MeshType>
bool Field<MeshType>::checkBoundaryConditions() const {
	/* if the number of boundary patches is not equal to the number of 
	 * boundary conditions, then it can't be correct anyway */
	if ( this->mesh().boundaryPatches().size() != m_boundaryConditions.size() )
		return false;
	/* check all non-empty boundary patches */
	int i {0};
	for ( const auto& patch : this->mesh().boundaryPatches() ){
		if ( !patch.empty() && !( m_boundaryConditions.at(i) ) )
			return false;
		++i;
	}
	return true;
}


template<typename MeshType>
void Field<MeshType>::resizeBCsToPatchesSize(){
	/* as many boundary conditions are needed as there are patches */
	auto nPatches { this->mesh().boundaryPatches().size() };
	if ( nPatches != m_boundaryConditions.size() )
		m_boundaryConditions.resize( nPatches );
}



/* explicit instantiation for all mesh types */
#define INSTANTIATE_FOR_ALL_MESHTYPES(TYPE) template class Field<TYPE>;
#include "meshInstantiator.hpp"


} // namespace hms
